package com.wondertek.poms.common.service.impl;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;

import com.wondertek.poms.common.exception.BusinessException;
import com.wondertek.poms.common.exception.MessageCodes;
import com.wondertek.poms.common.service.IService;

public abstract class ServiceImpl<T, PK> implements IService<T, PK> {

	@Autowired
	protected JpaRepository<T, PK> jpaRepository;
	
	protected static final int BATCH_SIZE = 300;

	/**
	 * 由于getOne方法会导致程序报 EntityNotFoundException，所以弃用
	 * findById 替换
	 */
	@Override
	public T get(PK id) {
		return findById(id);
	}
	
	@Override
	public T findById(PK id) {
		if(id == null){
			throw new BusinessException(MessageCodes.M980015);
		}
		Optional<T> optional = jpaRepository.findById(id);
		if(optional.isPresent()) {
    		return optional.get();
    	}
        throw new BusinessException(MessageCodes.M980011);
	}

	@Override
	public <S extends T> Page<S> findAll(S entity, int page, int rows, String order, String column) {
		Pageable pageable = getPageable(page - 1, rows, column, order);
		Example<S> example = Example.of(entity);
		Page<S> pages = jpaRepository.findAll(example, pageable);
		return pages;
	}

	@Override
	public List<T> findAll() {
		return jpaRepository.findAll();
	}
	
	public List<T> findAll(T t) {
		return jpaRepository.findAll(Example.of(t));
	}
	
	public List<T> findAll(Example<T> example) {
		return jpaRepository.findAll(example);
	}
	
	public List<T> findAll(Sort sort) {
		return jpaRepository.findAll(sort);
	}
	
	public List<T> findAll(String order, String column) {
		Sort sort = new Sort("desc".toLowerCase().equals(order) ? 
				Sort.Direction.DESC : Sort.Direction.ASC, column);
		return jpaRepository.findAll(sort);
	}
	
	public List<T> findAll(Example<T> example, Sort sort) {
		return jpaRepository.findAll(example, sort);
	}
	
	public Page<T> findAll(Pageable pageable) {
		return jpaRepository.findAll(pageable);
	}
	
	public Page<T> findAll(int page, int rows) {
		Pageable pageable = PageRequest.of(page, rows);
		return jpaRepository.findAll(pageable);
	}
	public Page<T> findAll(int page, int rows, String order, String column) {
		Pageable pageable = getPageable(page - 1, rows, column, order);
		return jpaRepository.findAll(pageable);
	}
	
	
	
	public List<T> findAll(Example<T> example, String order, String column) {
		Sort sort = new Sort("desc".toLowerCase().equals(order) ? 
				Sort.Direction.DESC : Sort.Direction.ASC, column);
		return jpaRepository.findAll(example, sort);
	}
	
	public List<T> findAll(T t, String order, String column) {
		Sort sort = new Sort("desc".toLowerCase().equals(order) ? 
				Sort.Direction.DESC : Sort.Direction.ASC, column);
		return jpaRepository.findAll(Example.of(t), sort);
	}
	
	public Optional<T> findOne(Example<T> example) {
		return jpaRepository.findOne(example);
	}
	
	public T findOne(T t) {
		Optional<T> optional = jpaRepository.findOne(Example.of(t));
		if (optional.isPresent()) {
    		return optional.get();
    	} else {
    		return null;
    	}
	}

	public T findModel(PK id) {
		if (id == null) {
			return null;
		}
		Optional<T> optional = jpaRepository.findById(id);
		if (optional.isPresent()) {
			return optional.get();
		}
		return null;
	}

	@Override
	@Transactional(readOnly = false)
	public void deleteById(PK id) {

		if(id == null){
			throw new BusinessException(MessageCodes.M980015);
		}
		
		jpaRepository.deleteById(id);
	}
	
	@Override
	@Transactional(readOnly = false)
	public T save(T entity) {
		return jpaRepository.save(entity);
	}
	
	@Override
	@Transactional(readOnly = false)
	public <S extends T> List<S> saveAll(Iterable<S> entities) {
		return jpaRepository.saveAll(entities);
	}
	
//	/**
//	 * 自增列会导致批量插入不起作用；
//	 * @GeneratedValue(strategy = GenerationType.IDENTITY)
//	 * 可以通过
//	@TableGenerator(name="init_data_generator",
//					table="tb_generator",
//		            pkColumnName="gen_name",
//		            valueColumnName="gen_value",
//		            pkColumnValue="INITDATA_PK",
//		            allocationSize=1)
//	@GeneratedValue(strategy = GenerationType.TABLE, generator="init_data_generator")躲避，但效率变慢了；
//	我认为：1.只能通过insert into biaoming values()()(); 在自己的service中去做了；
//	  	   2.最好能自己定义主键；从redis获取，uuid等方式。
//	 */
//	@Override
//	@Transactional
//	public <S extends T> Iterable<S> batchSave(Iterable<S> entities) {
//		Iterator<S> iterator = entities.iterator();
//		int index = 0;
//        while (iterator.hasNext()){
//            em.persist(iterator.next());
//            index++;
//            if (index % BATCH_SIZE == 0){
//                em.flush();
//                em.clear();
//            }
//        }
//        if (index % BATCH_SIZE != 0){
//            em.flush();
//            em.clear();
//        }
//        return entities;
//	}
//	
//	@Override
//	@Transactional(readOnly = false)
//	public <S extends T> Iterable<S> batchUpdate(Iterable<S> entities) {
//		Iterator<S> iterator = entities.iterator();
//        int index = 0;
//        while (iterator.hasNext()){
//            em.merge(iterator.next());
//            index++;
//            if (index % BATCH_SIZE == 0){
//                em.flush();
//                em.clear();
//            }
//        }
//        if (index % BATCH_SIZE != 0){
//            em.flush();
//            em.clear();
//        }
//        return entities;
//	}

	/**
	 * <p><b>Title:</b> getPageable</p>
	 * <p><b>Description:</b> 封装分页对象</p>
	 * @author douzi
	 * @param page 页码
	 * @param rows 条码个数
	 * @param column 排序列
	 * @param order 升/降序 asc/desc
	 * @return
	 */
	protected Pageable getPageable(int page, int rows, String column, String order) {
		Sort sort = new Sort("desc".toLowerCase().equals(order) ? 
				Sort.Direction.DESC : Sort.Direction.ASC, column);
		Pageable pageable = PageRequest.of(page, rows, sort);
		return pageable;
	}
}
